﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HdRSpielbox.muehle;
using System.Drawing;
using System.Windows.Forms;
using HdRSpielbox.Mühle;

namespace HdRSpielbox
{
    public class Muehle : Spiel
    {
        #region Variablen deklaration
        public Linie3[] Linien;
        public Knoepf[] Knoepfe;
        public PointM[] Points;
        public PointM Draging_point,Draging_backup_point;
        public Knoepf Draging_knopf;
        public int Phase,Player_1_set_count,Player_2_set_count;
        public Player Active_player, Winner;
        public bool Draging_active;
        public bool Muehle_gefunden;
        AI MeineKI;
        #endregion
        #region Implementation of Interface

        public void ki_next()
        {
            MeineKI.Next();
        }

        public override void Init()
        {
            Linien = new Linie3[16];
            Knoepfe = new Knoepf[18];
            Points = new PointM[24];
            if (Window != null)
            {
                Window.Hide();
            }
            else
            {
                Window = new Muehle1(this);
            }
            Phase = 0;
            Player_1_set_count = 0;
            Player_2_set_count = 0;
            Draging_active = false;

            int x = 50;
            int y = 50;
            int m_x = 600;
            int m_y = 600; 

            #region Define Lines & Create Points
            Linien[0] = new Linie3(new PointM(x, y), new PointM(x + (m_x) / 2, y), new PointM(m_x + x, y), false);
            Linien[1] = new Linie3(Linien[0].Pt3(), new PointM(x + m_x, y + m_y / 2), new PointM(m_x + x, m_y + y), true);
            Linien[3] = new Linie3(Linien[0].Pt1(), new PointM(x, y+(m_y) / 2), new PointM(x, m_y + y), true);
            Linien[2] = new Linie3(Linien[3].Pt3(), new PointM(x + m_x/2, y + m_y), Linien[1].Pt3(), false);
            Linien[4] = new Linie3(Linien[0].Pt2(), new PointM(x + (m_x) / 2, y + m_y / 6), new PointM(x + (m_x ) / 2, y + 2 * ((m_y) / 6)), true);
            Linien[6] = new Linie3(new PointM(x + m_x / 2, y + 4 * (m_y / 6)), new PointM(x + (m_x ) / 2, y + 5 * (m_y / 6)), Linien[2].Pt2(), true);
            Linien[5] = new Linie3(new PointM(x + 4 * (m_x / 6), y + m_y / 2), new PointM(x + 5 * (m_x / 6), y + m_y / 2), Linien[1].Pt2(), false);
            Linien[7] = new Linie3(Linien[3].Pt2(), new PointM(x + 1 * (m_x/ 6), y + m_y/ 2), new PointM(x + 2 * (m_x/ 6), y + (m_y) / 2), false);
            Linien[8] = new Linie3(new PointM(x + 1 * (m_x/ 6), y + 1 * (m_y/ 6)), Linien[4].Pt2(), new PointM(x + 5 * (m_x/ 6), y + 1 * (m_y / 6)), false);
            Linien[10] = new Linie3(new PointM(x + 1 * (m_x/ 6), y + 5 * (m_y/ 6)), Linien[6].Pt2(), new PointM(x + 5 * (m_x / 6), y + 5 * (m_y / 6)), false);
            Linien[9] = new Linie3(Linien[8].Pt3(), Linien[5].Pt2(), Linien[10].Pt3(), true);
            Linien[11] = new Linie3(Linien[8].Pt1(), Linien[7].Pt2(), Linien[10].Pt1(), true);
            Linien[12] = new Linie3(new PointM(x + 2 * (m_x/ 6), y + 2 * (m_y/ 6)), Linien[4].Pt3(), new PointM(x + 4 * (m_x / 6), y + 2 * (m_y / 6)), false);
            Linien[14] = new Linie3(new PointM(x + 2 * (m_x/ 6), y + 4 * (m_y/ 6)), Linien[6].Pt1(), new PointM(x + 4 * (m_x / 6), y + 4 * (m_y / 6)), false);
            Linien[13] = new Linie3(Linien[12].Pt3(), Linien[5].Pt1(), Linien[14].Pt3(), true);
            Linien[15] = new Linie3(Linien[12].Pt1(), Linien[7].Pt3(), Linien[14].Pt1(), true);
            int i = 0;
            foreach (Linie3 l in Linien)
            {
                if (!Points.Contains<PointM>(l.Pt1()))
                {
                    Points[i++] = l.Pt1();
                }
                if (!Points.Contains<PointM>(l.Pt2()))
                {
                    Points[i++] = l.Pt2();
                }
                if (!Points.Contains<PointM>(l.Pt3()))
                {
                    Points[i++] = l.Pt3();
                }
            }
            foreach (PointM p in Points)
            {
                foreach (Linie3 l in Linien)
                {
                    if (l.Pt1() == p || l.Pt2() == p || l.Pt3() == p)
                    {
                        if (p.Linie1 == null)
                        {
                            p.Linie1 = l;
                        }
                        else if (p.Linie2 == null)
                        {
                            p.Linie2 = l;
                        }
                    }
                }
            }
            #endregion

            MeineKI = new AI(this);
        }

        public override void Start()
        {
            for (int i = 0; i < 9; i++)
            {
                Knoepfe[i] = new Knoepf(Playerliste[0], Color.White);
                Knoepfe[i+9] = new Knoepf(Playerliste[1], Color.Black);
            }
            Draging_point = new PointM(0, 0);
            Window.Show();
            Active_player = Playerliste[0];
            Muehle_gefunden = false;
        }

        public override void Abort()
        {

        }

        #endregion
        #region Event Handlers
        public void Mouse_down(object sender, MouseEventArgs e)
        {
            switch (Phase)
            {
                case 0:
                    Set(e.Location);
                    break;
                case 1:
                    if (!Muehle_gefunden)
                    {
                        Move(e.Location);
                    }
                    else
                    {
                        Remove(e.Location);
                    }
                    break;
                case 2:
                    if (Muehle_gefunden)
                    {
                        Remove(e.Location);
                    }
                    Window.Refresh();
                    break;
            }
            Window.Refresh();
        }
        public void Mouse_up(object sender, MouseEventArgs e)
        {
            if (Draging_active && Phase == 1)
            {
                bool successfull = false;
                Draging_active = false;

                foreach (Linie3 l in Linien)
                {
                    PointM tmp_pt;
                    if ((tmp_pt = l.Collision(e.Location)) != null && !successfull)
                    {
                        if (!tmp_pt.set && tmp_pt.occupied == null && !successfull)
                        {
                            if (Reachable(tmp_pt, Draging_backup_point) || Count_remaining_knopefe(Active_player) == 3)
                            {
                                Draging_knopf.place = tmp_pt;
                                tmp_pt.set = true;
                                tmp_pt.occupied = Active_player;
                                Draging_backup_point.occupied = null;
                                Draging_backup_point.set = false;
                                successfull = true;
                            }
                        }
                    }
                    if (l.Muehle())
                    {
                        Muehle_gefunden = true;
                        Phase = 2;
                    }

                }
                if (!successfull)
                {
                    Draging_knopf.place = Draging_backup_point;
                }
                else if (!Muehle_gefunden)
                {
                    Active_player = Not_active_player();
                }
            }
            if ((Count_remaining_knopefe(Active_player) < 3 || locked(Active_player)) && Player_2_set_count == 9)
            {
                Phase = 3;
                Winner = Not_active_player();
            }
            Recalc_muehle();
            Window.Refresh();
        }
        #endregion
        #region Helper Functions
        private void Remove(Point point)
        {
            if ( Phase == 2)
            {
                bool removed = false;
                if (Can_remove_from_player(Not_active_player()))
                {
                    foreach (Linie3 l in Linien)
                    {
                        PointM tmp_pt;
                        if ((tmp_pt = l.Collision(point)) != null && !l.muehle_used && !tmp_pt.in_muehle)
                        {
                            if (/*tmp_pt.set &&*/ tmp_pt.occupied == Not_active_player())
                            {
                                removed = Remove_Knopf(removed, tmp_pt);
                            }
                        }
                    }
                }
                else
                {
                    removed = true;
                    Muehle_gefunden = false;
                }
                if (removed)
                {
                    Active_player = Not_active_player();
                    if (Player_2_set_count < 9)
                    {
                        Phase = 0;
                    }
                    else
                    {
                        Phase = 1;
                    }
                }
                Recalc_muehle();
                Window.Refresh();
            }
        }

        private bool Remove_Knopf(bool removed, PointM tmp_pt)
        {
            foreach (Knoepf k in Knoepfe)
            {
                if (k.place == tmp_pt && !k.place.in_muehle)
                {
                    k.owner = null;
                    k.set = false;
                    k.place = null;
                    removed = true;
                    Muehle_gefunden = false;
                    tmp_pt.occupied = null;
                    tmp_pt.set = false;
                }
            }
            return removed;
        }
        private Player Not_active_player()
        {
            if (Active_player == Playerliste[0]) { return Playerliste[1]; }
            else { return Playerliste[0]; }
        }
        private void Move(Point point)
        {
            if (Phase == 1)
            {
                foreach (Knoepf k in Knoepfe)
                {
                    if (k.place != null && k.owner == Active_player)
                    {
                        if (k.Collision(point))
                        {
                            Draging_backup_point = k.place;
                            k.place = Draging_point;
                            Draging_active = true;
                            Draging_knopf = k;
                        }
                    }
                }
            }
        }
        private void Set(Point point)
        {
            foreach (Linie3 l in Linien)
            {
                PointM tmp_pt;
                if ((tmp_pt = l.Collision(point)) != null)
                {
                    Setzen(l, tmp_pt);
                }
            }
        }

        private void Setzen(Linie3 l, PointM tmp_pt)
        {
            if (Active_player == Playerliste[0])
            {
                if (!tmp_pt.set && tmp_pt.occupied == null && Player_1_set_count < 9)
                {
                    Knoepfe[Player_1_set_count].place = tmp_pt;
                    Knoepfe[Player_1_set_count++].set = true;
                    tmp_pt.occupied = Playerliste[0];
                    if (l.Muehle())
                    {
                        Muehle_gefunden = true;
                        Phase = 2;
                    }
                    else
                    {
                        Active_player = Playerliste[1];
                    }
                }
            }
            else if (Active_player == Playerliste[1])
            {
                if (!tmp_pt.set && tmp_pt.occupied == null && Player_2_set_count < 9)
                {
                    Knoepfe[Player_2_set_count + 9].place = tmp_pt;
                    Knoepfe[Player_2_set_count++ + 9].set = true;
                    tmp_pt.occupied = Playerliste[1];
                    if (l.Muehle())
                    {
                        Muehle_gefunden = true;
                        Phase = 2;
                    }
                    else
                    {
                        Active_player = Playerliste[0];
                    }
                    if (Player_2_set_count == 9)
                    {
                        Phase = 1;
                    }
                }
            }
        }
        private void Setzen_von_Ai(PointM tmp_pt,PointM remv)
        {
            if (Active_player == Playerliste[0])
            {
                Knoepfe[Player_1_set_count].place = tmp_pt;
                Knoepfe[Player_1_set_count++].set = true;
            }
            else
            {
                Knoepfe[Player_2_set_count + 9].place = tmp_pt;
                Knoepfe[Player_2_set_count++ + 9].set = true;
            }
            if (remv != null)
            {
                Remove_Knopf(false, remv); 
            }

            tmp_pt.occupied = Active_player;
            Active_player = Not_active_player();
        }


        public bool Reachable(PointM p1, PointM p2)
        {
            bool result = false;
            foreach (Linie3 l in Linien)
            {
                if (l.Contains(p1) && l.Contains(p2) && ( !((l.Pt1() == p1 && l.Pt3() == p2 )||(l.Pt1() == p2 && l.Pt3() == p1 ))))
                {
                    result = true;
                }
            }
            return result;
        }
        public void Recalc_muehle()
        {
            foreach (Linie3 l in Linien)
            {
                if (l.Muehle())
                {
                    Muehle_gefunden = true;
                    Phase = 2;
                }
            }
        }
        public bool Can_remove_from_player(Player p)
        {
            bool result = false;
            foreach (Knoepf k in Knoepfe)
            {
                if (k.owner != null && k.place != null && k.owner.Equals(p) && k.set && !k.place.in_muehle)
                {
                    result = true;
                }
            }
            return result;
        }
        public int Count_remaining_knopefe(Player p)
        {
            int result = 0;
            foreach (Knoepf k in Knoepfe)
            {
                if (k.owner != null && k.place != null && k.owner.Equals(p) && k.set)
                {
                    result++;
                }
            }
            return result;
        }
        public bool locked(Player p)
        {
            bool result = true;
            if (Count_remaining_knopefe(p) > 3 && Player_2_set_count == 9)
            {
                foreach (Knoepf k in Knoepfe)
                {
                    if (k.owner == p && k.set && k.place != null && k.place.occupied == p)
                    {
                        if (k.place.Linie1.Movable(k.place) || k.place.Linie2.Movable(k.place))
                        {
                            result = false;
                        }
                    }
                }
            }
            else
            {
                result = false;
            }

            return result;
        }
        #endregion

        internal void Zug_ausführen(Züge Max_zug)
        {
            if (Max_zug.s != null && Max_zug.e != null)
            {
                Knoepfe.ToList().Find(p => p.place == Max_zug.s).place = Max_zug.e;//Knopf anpassen
                Max_zug.e.occupied = Max_zug.s.occupied;
                Max_zug.e.set = Max_zug.s.set;
                Max_zug.s.occupied = null;
                Max_zug.s.set = false;
            }
            else
            {
                Setzen_von_Ai(Max_zug.e,Max_zug.r);
            }
            if (Max_zug.r != null)
            {
                Knoepf tmp = Knoepfe.ToList().Find(p => p.place == Max_zug.r);//Knopf anpassen
                tmp.owner = null;
                tmp.place = null;
                tmp.set = false;
                Max_zug.r.occupied = null;
                Max_zug.r.set = false;
            }
            Window.Refresh();
        }
    }

    #region Helper-Classes
    public class PointM
    {
        #region Variablen Deklaration
        public Point p;
        public bool set;
        public Player occupied;
        public bool in_muehle;
        public Linie3 Linie1, Linie2;
        #endregion
        #region Functions
        public PointM(int i, int j)
        {
            p = new Point(i, j);
            set = false;
            in_muehle = false;
        }
        public void Chk_in_muehle()
        {
            if (Linie1.muehle_used || Linie2.muehle_used)
            {
                in_muehle = true;
            }
            else
            {
                in_muehle = false;
            }
        }
        #endregion
    }

    public class Knoepf
    {
        #region Variablen Deklaration
        public bool set;
        public Player owner;
        public PointM place;
        public Color color;
        #endregion
        #region Functions
        public Knoepf(Player p,Color color)
        {
            owner = p;
            this.color = color;
            set = false;
        }
        public void Draw(Graphics g)
        {
            if(set)
            {
                g.FillEllipse(new SolidBrush(color), place.p.X - 30, place.p.Y - 30, 60, 60);
            }
        }
        public bool Collision(Point pt)
        {
            if (pt.X > place.p.X - 30 && pt.X < place.p.X + 30 && pt.Y > place.p.Y - 30 && pt.Y < place.p.Y + 30)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion
    }

    public class Linie3
    {
        #region Variablen Deklaration
        private PointM pt1;
        private PointM pt2;
        private PointM pt3;
        public bool v;
        public bool muehle_used;
        #endregion
        #region Functions
        public Linie3(PointM p1, PointM p2, PointM p3, bool vertical)
        {
            pt1 = p1;
            pt2 = p2;
            pt3 = p3;
            v = vertical;
            muehle_used = false;
        }
        public void Draw(Graphics g, Brush b)
        {
            if (v)
            {
                g.DrawLine(new Pen(b, 16), new Point(pt1.p.X, pt1.p.Y - 8), new Point(pt3.p.X, pt3.p.Y + 8));
            }
            else
            {
                g.DrawLine(new Pen(b, 16), new Point(pt1.p.X - 8, pt1.p.Y), new Point(pt3.p.X + 8, pt3.p.Y));
            }
        }
        public PointM Collision(Point pt)
        {
            bool found = false;
            PointM match = pt1;
            if (pt.X > pt1.p.X - 30 && pt.X < pt1.p.X + 30 && pt.Y > pt1.p.Y - 30 && pt.Y < pt1.p.Y + 30)
            {
                found = true;
                match = pt1;
            }
            else if (pt.X > pt2.p.X - 30 && pt.X < pt2.p.X + 30 && pt.Y > pt2.p.Y - 30 && pt.Y < pt2.p.Y + 30)
            {
                found = true;
                match = pt2;
            }
            else if (pt.X > pt3.p.X - 30 && pt.X < pt3.p.X + 30 && pt.Y > pt3.p.Y - 30 && pt.Y < pt3.p.Y + 30)
            {
                found = true;
                match = pt3;
            }
            if (found)
            {
                return match;
            }
            else
            {
                return null;
            }
        }
        public bool Contains(PointM p)
        {
            if (pt1 == p || pt2 == p || pt3 == p)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        public bool Muehle()
        {
            bool result = false;
            if (pt1.occupied != null && pt2.occupied != null && pt3.occupied != null/* && pt1.set && pt2.set && pt3.set*/)
            {
                if (pt1.occupied.Equals(pt2.occupied) && pt2.occupied.Equals(pt3.occupied))
                {
                    if (!muehle_used)
                    {
                        result = true;
                        pt1.in_muehle = true;
                        pt2.in_muehle = true;
                        pt3.in_muehle = true;
                    }
                    muehle_used = true;
                }
            }
            else if (muehle_used)
            {
                muehle_used = false;
                pt1.Chk_in_muehle();
                pt2.Chk_in_muehle();
                pt3.Chk_in_muehle();
            }
            return result;
        }
        public bool Movable(PointM p)
        {
            bool result = false;

            if (pt1.Equals(p))
            {
                if (pt2.occupied == null)
                {
                    result = true;
                }
            }
            if (pt2.Equals(p))
            {
                if (pt1.occupied == null || pt3.occupied == null)
                {
                    result = true;
                }
            }

            if (pt3.Equals(p))
            {
                if (pt2.occupied == null)
                {
                    result = true;
                }
            }
            return result;
        }
        public bool Mühle_mit_Punkt(PointM Poin,Player Play) // Auf Speed Optimiert (AI)
        {
            if (pt1.occupied == null || pt2.occupied == null || pt3.occupied == null)
            {
                if (pt1 == Poin || pt2 == Poin || pt3 == Poin)
                {
                    if (((pt1.occupied == null) && (pt2.occupied == Play && pt2 != Poin) && (pt3.occupied == Play && pt3 != Poin)) ||
                        ((pt2.occupied == null) && (pt1.occupied == Play && pt1 != Poin) && (pt3.occupied == Play && pt3 != Poin)) ||
                        ((pt3.occupied == null) && (pt1.occupied == Play && pt1 != Poin) && (pt2.occupied == Play && pt2 != Poin)))
                    {
                        return true;
                    }
                }
                else return false;
            }
            return false;
        }
        #endregion
        #region Get-functions
        public PointM Pt1()
        {
            return pt1;
        }
        public PointM Pt2()
        {
            return pt2;
        }
        public PointM Pt3()
        {
            return pt3;
        }
        #endregion
    }
    #endregion
}
